﻿#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
using namespace std;

//C++98的C++语法中就有引⽤的语法，⽽C++11中新增了的右值引⽤语法特性，C++11之后我们之前学
//习的引⽤就叫做左值引⽤。⽆论左值引⽤还是右值引⽤，都是给对象取别名。

void test1() {
	//左值和右值
	
	//左值 ：可以取地址
	//左值是一个表示数据的表达式(如变量名或指针),一般是有持久状态，存储在内存中，我们
	// 可以获取它的地址，左值可以出现在赋值符号的左边，也可以出现在赋值符号右边。定义时
	// const修饰后的左值，不能给它赋值，但是可以取它的地址。
   
	//如：常见的左值：
	int a = 1; &a;
	int* p = new int(0);  &p;
	const int b = a; &b;
	vector<int> v({ 1,2,3,4,5,6 }); &v;
	v[0] = 0;  &v[0];

	//右值：不能取地址
	int x = 1, y = 2;

	//常见右值：
	//1.常量
	1; 2; 3; 4; 5; 6;
	//2.常量表达式
	x + y;
	//3.临时变量
	string("hello");
	vector<int>({ 1,2,3,4 });
	
	//报错：
	//&1; &(x + y);&string("hello");
	//&vector<int>({1,2,3,4});
}


   //左值引用和右值引用
void test2() {
    //如：
	int x = 1;
	int& r = x; //左值引用就是给左值取别名

	int&& rr = 10;  //右值引用就是给右值取别名
     
	//左值引用不能直接引用右值，但是const左值引用可以
	// 引用右值,即常量引用。
    //：
	// int& y = 10; //报错
	const int& y = 10;
	cout << y << endl;

	//右值引用不能直接引用左值，但是可以引用move(左值)
	//  move是库⾥⾯的⼀个函数模板，本质内部是进⾏强制类型转换(还有引用折叠)
	 //   不会改变元素的属性

	//int&& r1 = x; //报错
	int&& r1 = move(x); 
	&x;  //x被move后其属性不变仍是左值  //如：int i=0 ;int*p=(int*)i;  i仍然是一个int类型
	                                                     //只是产生了一个临时变量
														 
	//需要注意的是变量表达式都是左值属性，也就意味着⼀个右值被右值引⽤绑定后，右值引⽤变量变
	//量表达式的属性是左值。
	//即，对于上面的y：
	cout<<&y<<endl;

	//右值引用本身也是左值！
	int&& r2 = 30;
	cout << &r2 << endl; //可以取地址

	r2 = 10; // 因此右值引用的指向可以修改
	int& l = r2; //可以被左值引用引用。

}
void test3() {  //引用的一些功能
	// 对于临时变量 、临时对象、返回值，它们的生命周期就只在那一行
	// 引用可以延长它们的生命周期，直到那个引用销毁。
	std::string s1 = "Test";      

	const std::string& r2 = s1 + s1; // const 的左值引⽤延⻓ 临时变量（返回值）的⽣存期
	                               // r2 += "Test"; // 错误：不能通过到 const 的引⽤修改
	
	std::string&& r3 = s1 + s1; // OK：右值引⽤延⻓临时变量的⽣存期
		r3 += "Test"; // OK： 因为r3本身是左值

	std::cout << r3 << '\n';

}

//左值和右值的参数匹配：

//三者构成函数重载：
void f1(int& p) {
	cout <<"左值引用"<< endl;
}
void f1(const int& p) {
	//C++98中，我们实现⼀个const左值引⽤作为参数的函数，
	// 那么实参传递左值和右值都可以匹配
	cout << "const左值引用" << endl;
}
void f1(int&& p) {
	//C++11以后，分别重载左值引⽤、const左值引⽤、右值引⽤作为形参的f函数，那么实参是左值会
	//匹配f(左值引⽤)，实参是const左值会匹配f(const 左值引⽤)，实参是右值会匹配f(右值引⽤
	cout << "右值引用" << endl;
}

void test4() { //左值和右值的参数匹配
	int x = 10;
	f1(x);       
	f1(10);  //有右值引用函数则优先调用它，没有则调用const左值引用函数
	f1(move(x));  //调用右值引用

	int&& rr = 1;
	f1(rr);   //调用左值引用 ，因为右值引用本身是左值

}

int main() {
	//test1();
	test2();
	//test4();
	
	return 0;
}